﻿// ===================================
// <copyright>LogoUI Co.</copyright>
// <author>Vlad Spivak</author>
// <email>mailto:vlads@logoui.co.il</email>
// <created>20/00/10</created>
// <lastedit>20/00/10</lastedit>

// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the 'Software'), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
//
// <remarks>Part of this software based on various internet sources, mostly on the works
// of members of Wpf Disciples group http://wpfdisciples.wordpress.com/
// Also project may contain code from the frameworks: 
//        Nito 
//        OpenLightGroup
//        nRoute
// </remarks>
// ====================================================================================//


using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Reactive;
using System.Reactive.Linq;
#if !WinRT
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Data;
using System.Windows.Markup;
using System.Windows.Media;
using System.Windows.Input;
using System.Windows.Interactivity;
using LogoFX.Core;
using EventTrigger = System.Windows.Interactivity.EventTrigger;
#else
using LogoFX.Core;
using Windows.System;
using Windows.UI.Core;
using Windows.UI.Interactivity;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Markup;
using Windows.UI.Xaml.Media;
using EventTrigger = Windows.UI.Interactivity.EventTrigger;
using Key = Windows.System.VirtualKey;
#endif

namespace LogoFX.Infra.Interactivity
{
    [Obsolete("Use Microsoft.Expression.Interactivity.Input.KeyTrigger instead if possible")]
    public class KeyTrigger
#if !WinRT
		 : TriggerBase<UIElement>
#else
 : TriggerBase<FrameworkElement>
#endif
    {        
        private static readonly Duration DURATION_ZERO = new Duration(TimeSpan.Zero);

        public static readonly DependencyProperty KeyEventProperty =
           DependencyProperty.Register("KeyEvent", typeof(KeyEventType), typeof(KeyTrigger),
           new PropertyMetadata(KeyEventType.KeyUp));

        public static readonly DependencyProperty KeyCodeProperty =
            DependencyProperty.Register("KeyCode", typeof(int), typeof(KeyTrigger),
            new PropertyMetadata(0));

        public static readonly DependencyProperty KeyProperty =
            DependencyProperty.Register("Key", typeof(Key), typeof(KeyTrigger),
            new PropertyMetadata(Key.None));

#if !WinRT
        public static readonly DependencyProperty WithAltModifierProperty =
            DependencyProperty.Register("WithAltModifier", typeof(bool), typeof(KeyTrigger),
            new PropertyMetadata(false));

        public static readonly DependencyProperty WithControlModifierProperty =
            DependencyProperty.Register("WithControlModifier", typeof(bool), typeof(KeyTrigger),
            new PropertyMetadata(false));

        public static readonly DependencyProperty WithShiftModifierProperty =
            DependencyProperty.Register("WithShiftModifier", typeof(bool), typeof(KeyTrigger),
            new PropertyMetadata(false));

        public static readonly DependencyProperty WithWindowsModifierProperty =
            DependencyProperty.Register("WithWindowsModifier", typeof(bool), typeof(KeyTrigger),
            new PropertyMetadata(false));

        public static readonly DependencyProperty WithAppleModifierProperty =
            DependencyProperty.Register("WithAppleModifier", typeof(bool), typeof(KeyTrigger),
            new PropertyMetadata(false));
#endif
        public static readonly DependencyProperty SetIsHandledProperty =
            DependencyProperty.Register("SetIsHandled", typeof(bool), typeof(KeyTrigger),
            new PropertyMetadata(false));

        public static readonly DependencyProperty ThrottleProperty =
            DependencyProperty.Register("Throttle", typeof(Duration), typeof(KeyTrigger),
            new PropertyMetadata(DURATION_ZERO));

        private IObservable<EventPattern<KeyEventArgs>> _keyHandler;
        private bool _negate;

#region Properties

        
        public KeyEventType KeyEvent
        {
            get { return (KeyEventType)(GetValue(KeyEventProperty)); }
            set { SetValue(KeyEventProperty, value); }
        }

        
        public int KeyCode
        {
            get { return Convert.ToInt32(GetValue(KeyCodeProperty)); }
            set { SetValue(KeyCodeProperty, value); }
        }

        
        public Key Key
        {
            get { return (Key)(GetValue(KeyProperty)); }
            set { SetValue(KeyProperty, value); }
        }

#if !WinRT
        public bool WithAltModifier
        {
            get { return Convert.ToBoolean(GetValue(WithAltModifierProperty)); }
            set { SetValue(WithAltModifierProperty, value); }
        }

        
        public bool WithControlModifier
        {
            get { return Convert.ToBoolean(GetValue(WithControlModifierProperty)); }
            set { SetValue(WithControlModifierProperty, value); }
        }

        
        public bool WithShiftModifier
        {
            get { return Convert.ToBoolean(GetValue(WithShiftModifierProperty)); }
            set { SetValue(WithShiftModifierProperty, value); }
        }

        
        public bool WithWindowsModifier
        {
            get { return Convert.ToBoolean(GetValue(WithWindowsModifierProperty)); }
            set { SetValue(WithWindowsModifierProperty, value); }
        }

        /// <remarks>Ignored in non-Silverlight Apps</remarks>
        
        public bool WithAppleModifier
        {
            get { return Convert.ToBoolean(GetValue(WithAppleModifierProperty)); }
            set { SetValue(WithAppleModifierProperty, value); }
        }
#endif
        
        public bool SetIsHandled
        {
            get { return Convert.ToBoolean(GetValue(SetIsHandledProperty)); }
            set { SetValue(SetIsHandledProperty, value); }
        }

#if !WinRT        
        [TypeConverter(typeof(DurationConverter))]
#endif
        public Duration Throttle
        {
            get { return (Duration)(GetValue(ThrottleProperty)); }
            set { SetValue(ThrottleProperty, value); }

        }
        
        public bool Negate
        {
            get { return _negate; }
            set { _negate = value; }
        }

#endregion

#region Overrides

        protected override void OnAttached()
        {
            base.OnAttached();

            if (KeyEvent == KeyEventType.KeyUp)
            {
                _keyHandler = Observable.FromEventPattern<KeyEventHandler, KeyEventArgs>(
                                                      (h) => AssociatedObject.KeyUp += h,
                                                      (h) => AssociatedObject.KeyUp -= h);
            }
            else
            {
                _keyHandler = Observable.FromEventPattern<KeyEventHandler, KeyEventArgs>(
                                                      (h) => AssociatedObject.KeyDown += h,
                                                      (h) => AssociatedObject.KeyDown -= h);
            }

            // if throttling is required
            if (Throttle.HasTimeSpan && Throttle != TimeSpan.MaxValue && Throttle > TimeSpan.Zero)
                _keyHandler.Throttle(Throttle.TimeSpan);

            _keyHandler.Subscribe(OnKeyEvent);
        }

        protected override void OnDetaching()
        {
           base.OnDetaching();
         //  _keyHandler.Dispose();
        }

#endregion

#region Handler

        private void OnKeyEvent(EventPattern<KeyEventArgs> ev)
        {

#if SILVERLIGHT
            bool _isMatch = (KeyCode != 0) ? (KeyCode == ev.EventArgs.PlatformKeyCode) : (ev.EventArgs.Key == Key);
#elif !WinRT
            bool _isMatch = (KeyCode != 0) ? (KeyCode == Convert.ToInt32(ev.EventArgs.Key)) : (ev.EventArgs.Key == Key);
#else
            bool _isMatch = (KeyCode != 0) ? (KeyCode == Convert.ToInt32(ev.EventArgs.VirtualKey)) : (ev.EventArgs.VirtualKey == Key);
#endif

#if !WinRT
            // match the modifiers
            var _modifiers = Keyboard.Modifiers;

            if (WithAltModifier && (_modifiers & ModifierKeys.Alt) != ModifierKeys.Alt) _isMatch = false;
            if (!WithAltModifier && (_modifiers & ModifierKeys.Alt) == ModifierKeys.Alt) _isMatch = false;

            if (WithControlModifier && (_modifiers & ModifierKeys.Control) != ModifierKeys.Control) _isMatch = false;
            if (!WithControlModifier && (_modifiers & ModifierKeys.Control) == ModifierKeys.Control) _isMatch = false;

            if (WithShiftModifier && (_modifiers & ModifierKeys.Shift) != ModifierKeys.Shift) _isMatch = false;
            if (!WithShiftModifier && (_modifiers & ModifierKeys.Shift) == ModifierKeys.Shift) _isMatch = false;

            if (WithWindowsModifier && (_modifiers & ModifierKeys.Windows) != ModifierKeys.Windows) _isMatch = false;
            if (!WithWindowsModifier && (_modifiers & ModifierKeys.Windows) == ModifierKeys.Windows) _isMatch = false;

#if SILVERLIGHT
            if (WithAppleModifier && (_modifiers & ModifierKeys.Apple) != ModifierKeys.Apple) _isMatch = false;
            if (!WithAppleModifier && (_modifiers & ModifierKeys.Apple) == ModifierKeys.Apple) _isMatch = false;
#endif
#endif
            // we flow down till here, as we might need to negate the match
            if (Negate) _isMatch = !_isMatch;
            if (!_isMatch) return;

            // raise
#if !WinRT
            base.InvokeActions(ev.EventArgs.Key);
#else
            base.InvokeActions(ev.EventArgs.VirtualKey);
#endif

            // handled
            if (SetIsHandled) ev.EventArgs.Handled = true;
        }

#endregion

    }
}
